/*
 * DRIVER functions of `HRV64Generator` and `RV64Funct`
 * to generate RV64 code from HXD1 IR.
 */

#include "HRV64Generator.h"
#include "HExceptions.h"

// Don't use these flags of temp!
#define fassigned ERROR
#define frefed ERROR
#define lrefed ERROR

HRV64Generator hxd1_rv64gen;

extern std::ofstream log_output;

RV64GFR HRV64Generator::GPR_order[] = {
	RV64GFR::T0, RV64GFR::T1, RV64GFR::T2, RV64GFR::T3, RV64GFR::T4,
	RV64GFR::T5, RV64GFR::T6, RV64GFR::S0, RV64GFR::S1, RV64GFR::S2,
	RV64GFR::S3, RV64GFR::S4, RV64GFR::S5, RV64GFR::S6, RV64GFR::S7,
	RV64GFR::S8, RV64GFR::S9, RV64GFR::S10, RV64GFR::S11,
	RV64GFR::A7, RV64GFR::A6, RV64GFR::A5, RV64GFR::A4, RV64GFR::A3,
	RV64GFR::A2, RV64GFR::A1, RV64GFR::A0,
	/*
	 * End of allocable registors. If `ZERO` is found while scanning
	 * the order array, the scanning should stop.
	 */
	RV64GFR::ZERO, RV64GFR::ZERO, RV64GFR::ZERO, RV64GFR::ZERO, RV64GFR::ZERO
};

RV64GFR HRV64Generator::FPR_order[] = {
	RV64GFR::FT0, RV64GFR::FT1, RV64GFR::FT2, RV64GFR::FT3, RV64GFR::FT4, RV64GFR::FT5,
	RV64GFR::FT6, RV64GFR::FT7, RV64GFR::FT8, RV64GFR::FT9, RV64GFR::FT10, RV64GFR::FT11,
	RV64GFR::FS0, RV64GFR::FS1, RV64GFR::FS2, RV64GFR::FS3, RV64GFR::FS4, RV64GFR::FS5,
	RV64GFR::FS6, RV64GFR::FS7, RV64GFR::FS8, RV64GFR::FS9, RV64GFR::FS10, RV64GFR::FS11,
	RV64GFR::FA7, RV64GFR::FA6, RV64GFR::FA5, RV64GFR::FA4, RV64GFR::FA3, RV64GFR::FA2,
	RV64GFR::FA1, RV64GFR::FA0
};

/*
 * When a function has big stack (bigger than the imm of `addi sp`),
 * accessing the stack would need an auxiliary GFR.
 */
RV64GFR HRV64Generator::aux_reg = RV64GFR::RA;

/*
 * Default operand size of integer instruction which can be `D` or `W`.
 * If it is set as `D`, normal integer calculations will be realized
 * by RV64 instruction (for example, `add` rather than `addw`).
 */
RV64Opsz HRV64Generator::integer_inst_size = RV64Opsz::D;

// Construct a RV64Funct with is `_tdescs` and `_rdescs` initialized
RV64Funct::RV64Funct(size_t hirfidx, const HIRCode &ircode, bool is_builtin)
{
#define PRINT_LOG(log)							\
	if (opts.log_file != nullptr)				\
		log_output << log << std::endl;

	ircode_ = &ircode;
	hirfidx_ = hirfidx;
	id_ = ircode.funct(hirfidx).id;
	if (!is_builtin)
	{
		PRINT_LOG("Prepare RV64 generation for function \'"
				<< id_ << "()\'...\n");
		tdescs_.resize(ircode.funct(hirfidx).temps.size());
		frame_base_ = static_top_ = FRAME_BASE;
		bb_stacks_.resize(ircode.funct(hirfidx).basic_blocks.size());
		epilogue_ = ircode.funct(hirfidx).temps.size();
		for (auto & bs : bb_stacks_)
		{	// initialize
			bs.sp = frame_base_;
			bs.nargs_on_stack = 0u;
		}
		reset_desc();
		allocate_static();
		// Build IG and do register allocation!
		if (opts.opt_gra)
		{
			PRINT_LOG("Building original interference graph...");
			build_interference_graph();
			if (opts.log_file != nullptr)
			{
				intgraph_.print(log_output, '\0');
				log_output << std::endl;
			}
			reg_alloc();
		}
		// save temps of arguments to the stack
		init_argumens(ircode_->funct(hirfidx_).nparams);

		PRINT_LOG('\n');
	}

#undef PRINT_LOG
}

/*
 * Save temps of arguments to the stack and set their descs.
 * Step 1: for every argument temp `t`, if it is non-local,
 * 		set its temp desc as `REG` or `STACK` according to RV64 ABI;
 * Step 2: if non-local `t` is referenced (thus allocated in `allocate_static()`)
 * 		and its desc is `REG`, call `reg_spill()` to write it to stack.
 */
void RV64Funct::init_argumens(size_t nargs)
{
	HIRTempno i;
	bool flag_not_used_argument;
	size_t nargs_GPR = 0ul, nargs_FPR = 0ul, nargs_stack = 0ul;
	for (i = 0u; i < nargs; ++i)
	{
		const HIRTemp &ta = ircode_->funct(hirfidx_).temps.at(i);
		if (ta.localof != INVALID_BB_IDX)
			/*
			 * Skip local temp or temp that is never referenced.
			 * NOTE: if the argument is not referenced at all,
			 * its `localof` will be set as 0 according to the new rule.
			 */
			//continue;
			flag_not_used_argument = true;
			/*
			 * An argument will occupy a reg or stack space
			 * even if it is not referenced, so simply
			 * `continue` is wrong!
			 */
		else
			flag_not_used_argument = false;

		if (ta.type == HIRBtype::DOUBLE || ta.type == HIRBtype::FLOAT)
		{	// floating-point arguments
			if (nargs_FPR >= RV64_AFPR)
			{	// should be passed on the stack
				tdescs_.at(i).status = TempDesc::STACK;
				tdescs_.at(i).stloc = frame_base_ + nargs_stack * RV64_GFR_SIZE;
				++nargs_stack;
			}
			else
			{	// should be passed on FPR
				if ((tdescs_.at(i).status & TempDesc::ALLOCATED) == 0)
				{
					tdescs_.at(i).status = TempDesc::REG;
					tdescs_.at(i).reg = RV64GFR(unsigned(RV64GFR::FA0) + nargs_FPR);
				}
				else
					if (flag_not_used_argument == false &&
						tdescs_.at(i).reg != RV64GFR(unsigned(RV64GFR::FA0) + nargs_FPR))
						throw std::logic_error("Conflicting reg for arg %" +
							std::to_string(i));
				++nargs_FPR;
				/*
				 * `bbidx` of `temp_writeback()` will not be used, so we can
				 * set it invalid. If it is used, exception would be thrown
				 * to show that there is something wrong!
				 */
				rdescs_[unsigned(tdescs_.at(i).reg)].status = RegDesc::TEMP;
				rdescs_[unsigned(tdescs_.at(i).reg)].tempno = i;
				if (!flag_not_used_argument)
					if ((tdescs_.at(i).status & TempDesc::ALLOCATED) == 0)
					/*
					 * If the argument is not allocated reg,
					 * write it to stack!
					 */
						temp_writeback(i, INVALID_BB_IDX);
			}
		}
		else
		{
			if (nargs_GPR >= RV64_AGPR)
			{	// should be passed on the stack
				tdescs_.at(i).status = TempDesc::STACK;
				tdescs_.at(i).stloc = frame_base_ + nargs_stack * RV64_GFR_SIZE;
				++nargs_stack;
			}
			else
			{	// should be passed on GPR
				if ((tdescs_.at(i).status & TempDesc::ALLOCATED) == 0)
				{
					tdescs_.at(i).status = TempDesc::REG;
					tdescs_.at(i).reg = RV64GFR(unsigned(RV64GFR::A0) + nargs_GPR);
				}
				else
					if (flag_not_used_argument == false &&
						tdescs_.at(i).reg != RV64GFR(unsigned(RV64GFR::A0) + nargs_GPR))
						throw std::logic_error("Conflicting reg for arg %" +
							std::to_string(i));
				++nargs_GPR;
				rdescs_[unsigned(tdescs_.at(i).reg)].status = RegDesc::TEMP;
				rdescs_[unsigned(tdescs_.at(i).reg)].tempno = i;
				if (!flag_not_used_argument)
					if ((tdescs_.at(i).status & TempDesc::ALLOCATED) == 0)
						temp_writeback(i, INVALID_BB_IDX);
			}
		}
		if (tdescs_.at(i).status == TempDesc::STACK
			&& (tdescs_.at(i).status & TempDesc::ALLOCATED) != 0)
		{	/*
			 * If the stack argument is allocated a reg, load it from stack
			 * at the beginning of the function.
			 */
			load_temp_to_reg(i, tdescs_.at(i).reg);
			if (!rdescs_[uint32_t(tdescs_.at(i).reg)].flag_allocated)
				exc_reg_not_allocated(i, tdescs_.at(i).reg);
			/*
				* These two operations seems meaningless because
				* the desc will be reset at the beginning of any BB...
				*/
			rdescs_[uint32_t(tdescs_.at(i).reg)].status = RegDesc::TEMP;
			rdescs_[uint32_t(tdescs_.at(i).reg)].tempno = i;
		}
	}
}

/*
 * Reset desc when initializing a function or entering a BB.
 * Set `bbidx` invalid for the first situation.
 */
void RV64Funct::reset_desc(uint32_t bbidx)
{
	// Reset temp descriptors
	size_t ntemps = ircode_->funct(hirfidx_).temps.size(),
		nalloca = ircode_->funct(hirfidx_).nalloca, i;
	for (i = 0ul; i < ntemps; ++i)
	{
		if (bbidx != INVALID_BB_IDX &&
			(tdescs_.at(i).status & TempDesc::ALLOCATED) != 0)
			tdescs_.at(i).status = TempDesc::ALLOCATED | TempDesc::REG;
		else
		{	/*
			 * If the temp is not allocated with a reg, we should clear
			 * `REG` and `STATIC` flags of all temps and all flags
			 * of local temps when entering and leaving a BB.
			 */
			if (ircode_->funct(hirfidx_).temps.at(i).localof != INVALID_BB_IDX)
				// clear local temp's desc
				tdescs_.at(i).status = TempDesc::UNKNOWN;
			else	// clear non-local temp's `REG` and `STATIC`
				tdescs_.at(i).status &= ~int32_t(TempDesc::REG | TempDesc::STATIC);
		}
	}
	for (i = 0ul; i < nalloca; ++i)
	{/*
	 * However, for the temps assigned at the beginning of a function
	 * by `ALLOCA` instruction, we keep their `STATIC` flag.
	 */
		HIRTempno alloca_dest = ircode_->funct(hirfidx_).ircode.at(i).get_dest();
		if (alloca_dest == INVALID_TEMP_NO)
			throw std::logic_error(
				std::string("inst ") + std::to_string(i) + " of funct "
				+ std::to_string(hirfidx_) + "has invalid dest"
			);
		tdescs_.at(alloca_dest).status = TempDesc::STATIC;
	}

	// Reset reg descriptors
	/*
	 * Set all regs listed in `GPR_order[]`,
	 * `FPR_order[]` as `FREE` and others `RESERVED`.
	 */
	for (auto & rd : rdescs_)
		rd.status = RegDesc::RESERVED;
	if (bbidx == INVALID_BB_IDX)
		for (auto & rd : rdescs_)
			rd.flag_allocated = false;
	for (i = 0ul; i < unsigned(RV64GFR::NGPR)
		&& HRV64Generator::GPR_order[i] != RV64GFR::ZERO; ++i)
		rdescs_[unsigned(HRV64Generator::GPR_order[i])].status = RegDesc::FREE;
	for (i = 0ul; i < unsigned(RV64GFR::NGFR) - unsigned(RV64GFR::NGPR)
		&& HRV64Generator::FPR_order[i] != RV64GFR::ZERO; ++i)
		rdescs_[unsigned(HRV64Generator::FPR_order[i])].status = RegDesc::FREE;

	if (bbidx != INVALID_BB_IDX)
	{
		// set desc for live temps at the beginning of a BB
		for (auto livet : ircode_->funct(hirfidx_).basic_blocks.at(bbidx).livevar_in)
			if (tdescs_.at(livet).status & TempDesc::ALLOCATED)
			{
				if (rdescs_[uint32_t(tdescs_.at(livet).reg)].flag_allocated == false)
					exc_reg_not_allocated(livet, tdescs_.at(livet).reg);
				rdescs_[uint32_t(tdescs_.at(livet).reg)].status = RegDesc::TEMP;
				rdescs_[uint32_t(tdescs_.at(livet).reg)].tempno = livet;
			}
	}
}

/*
 * Allocate one unit on static stack location (for one saved reg,
 * non-local temp etc.) or BB local (if `bbidx` is valid).
 * Make sure that `alignment` is power of 2.
 * But the alignment of `$sp` is at least 16 in RV64!
 * Return the allocated stack address.
 */
stloc_t RV64Funct::allocate_unit(size_t size, size_t alignment, uint32_t bbidx)
{
	stloc_t ret_addr;
	if (bbidx == INVALID_BB_IDX)
		ret_addr = static_top_ = rounddown(static_top_ - size, alignment);
	else
	{
		if (bb_stacks_.at(bbidx).sp == frame_base_)
			/*
			 * Initialize it if necessary.
			 * But now is UNnecessary as it is initiailzed in
			 * `generate_bb_code()`.
			 */
			bb_stacks_.at(bbidx).sp = static_top_;
		ret_addr = bb_stacks_.at(bbidx).sp
			= rounddown(bb_stacks_.at(bbidx).sp - size, alignment);
	}
	if (ret_addr >= frame_base_)	// overflow
		throw UnsupportedSituation("stack overflow: 0");
	return ret_addr;
}

/*
 * Allocate for all static data (data by `ALLOCA`, non-local temps)
 * EXCEPT saved regs (ignoring them).
 * So the address is calculated WITHOUT considering saved regs.
 */
void RV64Funct::allocate_static()
{
	size_t ntemps = ircode_->funct(hirfidx_).temps.size(),
		nalloca = ircode_->funct(hirfidx_).nalloca, i;
	// Allocate space for `ALLOCA` instructions
	for (i = 0ul; i < nalloca; ++i)
	{
		HIRTempno alloca_dest = ircode_->funct(hirfidx_).ircode.at(i).get_dest();
		uint32_t size = hirsize(ircode_->funct(hirfidx_).ircode.at(i).type),
			alignment = ircode_->funct(hirfidx_).ircode.at(i).alignment;
		// `reset_desc()` has checked that `alloca_dest` is valid
		tdescs_.at(alloca_dest).status = TempDesc::STATIC;
		tdescs_.at(alloca_dest).stval = allocate_unit(size, alignment);
	}
	// Allocate space for non-local temps
	for (i = 0ul; i < ntemps; ++i)
	{
		uint32_t tsize;
		const HIRTemp &temp = ircode_->funct(hirfidx_).temps.at(i);
		if (temp.localof != INVALID_BB_IDX)
			// skip local temps including temps that are not used
			continue;
		if (temp.type == HIRBtype::VOID)
			// skip label
			continue;
		if (tdescs_.at(i).status & TempDesc::STATIC)
			// skip temp that has stack value (by `ALLOCA`)
			continue;
		tsize = hirsize(temp.type);
		/*
		 * NOTE: if the `.type` is invalid, `tsize` will be zero.
		 * Then the `allocate_unit()` will return zero because of `rounddown()`,
		 * thus will lead to "stack overflow" exception at the next iteration!
		 */
		tdescs_.at(i).stloc = allocate_unit(tsize, tsize);
		/*
		 * Set the temp desc as `STACK` so that if a non-local
		 * temp is used without initialization, it would be found
		 * rather than cause "Cannot find temp" exception to be thrown.
		 */
		tdescs_.at(i).status = TempDesc::STACK;
	}
	static_top_ = rounddown(static_top_, RV64_SP_ALIGN);
}

// Initialize the generator from HXD1 IR
void HRV64Generator::init_from_IR(const HIRCode &ircode)
{
	// Initialize all external variables
	size_t nxvars = ircode.extvars().size(), i, l;
	size_t iralignment, bsize;
	xsyms_.resize(nxvars);
	for (i = 0ul; i < nxvars; ++i)
	{
		if (ircode.extvar(i).is_const)
			xsyms_.at(i).sttype = RV64XSymbol::RODATA;
		else
			xsyms_.at(i).sttype = RV64XSymbol::DATA;
		if ((xsyms_.at(i).data = ircode.extvar(i).data) == nullptr)
			xsyms_.at(i).sttype = RV64XSymbol::BSS;
		iralignment = ircode.extvar(i).alignment;
		for (l = 0ul; l <= RV64_ALIGN_MAX; ++l)
			if (iralignment == (1ul << l))
			{
				xsyms_.at(i).align = l;
				break;
			}
		// If it is not power of 2, throw exception
		if (l > RV64_ALIGN_MAX)
			throw std::logic_error(
				std::string("invalid alignment ") + std::to_string(iralignment)
				+ " of xvar " + std::to_string(i)
			);
		bsize = hirsize(ircode.extvar(i).btype);
		xsyms_.at(i).size = bsize * ircode.extvar(i).length;
		xsyms_.at(i).id = ircode.extvar(i).id;
	}
	// Initialize all functions from HXD1 IR
	size_t nfuncs = ircode.functs().size();
	nbuiltin_in_IR_ = ircode.nbuiltin();
	for (i = 0ul; i < nfuncs; ++i)
		// ignore builtin functions
		funcs_.push_back(RV64Funct(i, ircode, i < nbuiltin_in_IR_));
}

/*
 * Search for a function by its id.
 * If found, return its index in `_funcs[]`, otherwise return `SIZE_MAX`.
 */
size_t HRV64Generator::search_func(const std::string &id) const
{
	size_t i;
	for (i = 0ul; i < funcs_.size(); ++i)
		if (funcs_.at(i).id() == id)
			return i;
	return SIZE_MAX;
}
/*
 * When we leave a BB, all live temporaries that are assigned
 * in this BB but not allocated registers must be
 * written back to stack.
 * This function will append instructions to do this.
 */
void RV64Funct::save_temps_to_stack(uint32_t bbidx)
{
	const BasicBlock &bb = ircode_->funct(hirfidx_).basic_blocks.at(bbidx);
	for (auto & atemp : bb.asstemps)
	{
		if (ircode_->funct(hirfidx_).basic_blocks.at(bbidx).
				livevar_out.count(atemp.first) == 0ul
			|| (tdescs_.at(atemp.first).status & TempDesc::ALLOCATED) != 0)
			// Skip temp that is not live or has been allocated a register
			continue;
		if ((tdescs_.at(atemp.first).status & TempDesc::STACK) == 0)
			/*
			 * If the temp's newest value is not on the stack,
			 * call `temp_writeback()` to write it back.
			 * NOTE: if the temp's `status` doesn't have `REG`,
			 * exception will be thrown in this function!
			 */
			temp_writeback(atemp.first, bbidx);
	}
}

// Build HXD1D interference graph
void RV64Funct::build_interference_graph(void)
{
	intgraph_.build_from_hirfunct(ircode_->funct(hirfidx_));
}

/*
 * Return the set of temps in working interference graph
 * that are coalesced with temp `t`.
 * This is used to decide whether the
 * temp should be saved and restored when generating RV64 code
 * for `call` instrcution.
 */
std::set<HIRTempno> RV64Funct::get_temps_sharing_reg(HIRTempno t) const
{
	std::set<HIRTempno> temps;
	for (auto & vtx : wintgraph_.vertexes())
		if (vtx.valid && vtx.allocated && vtx.temps.count(t) > 0ul)
		{
			for (auto st : vtx.temps)
				if (st != t)
					temps.insert(st);
			break;
		}
	return temps;
}

uint64_t RV64Funct::append_label(uint64_t labelno)
{
	RV64Inst inst(RV64Oper::LABEL);
	inst.labelno = labelno;
	code_.push_back(inst);
	return 0ul;
}
uint64_t RV64Funct::append_arlo(RV64Oper op, RV64GFR src1, RV64GFR src2,
	RV64GFR dest, RV64Opsz sz, std::string *cominfo)
{
	if (sz == RV64Opsz::DEFAULT)
		sz = HRV64Generator::integer_inst_size;
	RV64Inst inst(op, sz, cominfo);
	inst.src1 = src1; inst.src2 = src2; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}
size_t RV64Funct::append_arloi(RV64Oper op, RV64GFR src1, int64_t imm12,
	RV64GFR dest, RV64Opsz sz, std::string *cominfo)
{
	if (sz == RV64Opsz::DEFAULT)
		sz = HRV64Generator::integer_inst_size;
	RV64Inst inst(op, sz, cominfo);
	inst.src1 = src1; inst.imm12 = imm12; inst.dest = dest;
	code_.push_back(inst); return code_.size() - 1ul;
}
uint64_t RV64Funct::append_slt(RV64Oper op, RV64GFR src1, RV64GFR src2, RV64GFR dest)
{
	RV64Inst inst(op);
	inst.src1 = src1; inst.src2 = src2; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_slti(RV64Oper op, RV64GFR src1, int64_t imm12, RV64GFR dest)
{
	RV64Inst inst(op);
	inst.src1 = src1; inst.imm12 = imm12; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}
size_t RV64Funct::append_load(RV64Opsz sz, RV64GFR base, RV64GFR dest,
	int64_t off12, std::string *cominfo)
{
	RV64Inst inst(RV64Oper::LOAD, sz, cominfo);
	inst.base = base; inst.dest = dest; inst.off12 = off12;
	code_.push_back(inst); return code_.size() - 1u;
}
size_t RV64Funct::append_store(RV64Opsz sz, RV64GFR base, RV64GFR src2,
	int64_t off12, std::string *cominfo)
{
	RV64Inst inst(RV64Oper::STORE, sz, cominfo);
	inst.base = base; inst.src2 = src2; inst.off12 = off12;
	code_.push_back(inst); return code_.size() - 1u;
}
uint64_t RV64Funct::append_ldaddr(RV64GFR dest, uint64_t xsymidx)
{
	RV64Inst inst(RV64Oper::LA);
	inst.dest = dest; inst.xsymidx = xsymidx;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_ldsymb(RV64Opsz sz, RV64GFR dest, uint64_t xsymidx)
{
	RV64Inst inst(RV64Oper::LDs, sz);
	inst.dest = dest; inst.xsymidx = xsymidx;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_ldimm(RV64GFR dest, int64_t imm64, std::string *cominfo)
{
	RV64Inst inst(RV64Oper::LI);
	inst.dest = dest; inst.imm64 = imm64; inst.cominfo = cominfo;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_jump(uint64_t labelno)
{
	RV64Inst inst(RV64Oper::J);
	inst.labelno = labelno;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_call(uint64_t functidx)
{
	RV64Inst inst(RV64Oper::CALL);
	inst.functidx = functidx;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_cbranch(RV64Oper op, RV64GFR src1, RV64GFR src2,
	uint64_t lanelno)
{
	RV64Inst inst(op);
	inst.src1 = src1; inst.src2 = src2; inst.labelno = lanelno;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_ret(void)
{
	code_.push_back(RV64Inst(RV64Oper::RET));
	return 0ul;
}
uint64_t RV64Funct::append_move(RV64GFR src1, RV64GFR dest)
{
	RV64Inst inst(RV64Oper::MV);
	inst.src1 = src1; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}
size_t RV64Funct::append_fload(RV64Opsz sz, RV64GFR base, RV64GFR fdest,
	int64_t off12, std::string *cominfo)
{
	RV64Inst inst(RV64Oper::FLOAD, sz, cominfo);
	inst.base = base; inst.fdest = fdest; inst.off12 = off12;
	code_.push_back(inst); return code_.size() - 1u;
}
size_t RV64Funct::append_fstore(RV64Opsz sz, RV64GFR base, RV64GFR fsrc2,
	int64_t off12, std::string *cominfo)
{
	RV64Inst inst(RV64Oper::FSTORE, sz, cominfo);
	inst.base = base; inst.fsrc2 = fsrc2; inst.off12 = off12;
	code_.push_back(inst); return code_.size() - 1u;
}
uint64_t RV64Funct::append_farith(RV64Opsz sz, RV64Oper op, RV64GFR fsrc1,
	RV64GFR fsrc2, RV64GFR fdest, std::string *cominfo)
{
	RV64Inst inst(op, sz, cominfo);
	inst.fsrc1 = fsrc1; inst.fsrc2 = fsrc2; inst.fdest = fdest;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_fmove(RV64Opsz sz, RV64Oper op, RV64GFR fsrc1, RV64GFR fdest)
{
	RV64Inst inst(op, sz);
	inst.fsrc1 = fsrc1; inst.fdest = fdest;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_fmv2i(RV64Opsz sz, RV64GFR fsrc1, RV64GFR dest)
{
	RV64Inst inst(RV64Oper::FMV2I, sz);
	inst.fsrc1 = fsrc1; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_fmv4i(RV64Opsz sz, RV64GFR src1, RV64GFR fdest)
{
	RV64Inst inst(RV64Oper::FMV4I, sz);
	inst.src1 = src1; inst.fdest = fdest;
	code_.push_back(inst); return 0ul;
}
uint64_t RV64Funct::append_fcomp(RV64Opsz sz, RV64Oper op, RV64GFR fsrc1,
	RV64GFR fsrc2, RV64GFR dest)
{
	RV64Inst inst(op, sz);
	inst.fsrc1 = fsrc1; inst.fsrc2 = fsrc2; inst.dest = dest;
	code_.push_back(inst); return 0ul;
}

// Generate code for all functions
void HRV64Generator::generate()
{
	size_t i;
	for (i = nbuiltin_in_IR_; i < funcs_.size(); ++i)
		funcs_.at(i).generate_code();
}

/*
 * When a temp `t`'s desc shows that it has been allocated with
 * a reg `r` but `r`'s desc has no `flag_allocated`,
 * throw an logic error exception!
 */
void RV64Funct::exc_reg_not_allocated(HIRTempno t, RV64GFR r)
{
	throw std::logic_error("temp " + std::to_string(t) +
		" is allocated with reg " +
		std::to_string(uint32_t(r)) +
		" but its flag_allocated is invalid");
}